import subprocess
import platform
import re
import concurrent.futures
import tkinter as tk
from tkinter import ttk,messagebox
import threading



class NetworkScannerGUI:
    def __init__(self, master):
        self.master = master
        master.title("网络扫描器 v1.0")
        
        # 创建输入区域
        input_frame = ttk.Frame(master, padding="10")
        input_frame.pack(fill=tk.X)
        
        ttk.Label(input_frame, text="目标IP/网段:").grid(row=0, column=0, sticky=tk.W)
        self.ip_entry = ttk.Entry(input_frame, width=25)
        self.ip_entry.grid(row=0, column=1, padx=5)
        self.scan_button = ttk.Button(input_frame, text="开始扫描", command=self.start_scan)
        self.scan_button.grid(row=0, column=2, padx=5)
         # 新增提示标签
        ttk.Label(input_frame, text="输入示例：192.168.1.100（单个IP）或 192.168.1（网段）", 
                 foreground="gray").grid(row=1, column=0, columnspan=3, pady=5, sticky=tk.W)
        # 创建结果显示表格
        self.result_tree = ttk.Treeview(master, columns=('IP', 'MAC', '平台'), show='headings')
        self.result_tree.heading('IP', text='IP地址')
        self.result_tree.heading('MAC', text='MAC地址')
        self.result_tree.heading('平台', text='操作系统')
        self.result_tree.pack(expand=True, fill=tk.BOTH, padx=10, pady=5)
        
        # 进度条
        self.progress = ttk.Progressbar(master, mode='indeterminate')
    
    def validate_input(self, ip_str):
        """验证输入的IP格式"""
        if re.match(r"^\d{1,3}\.\d{1,3}\.\d{1,3}(\.\d{1,3})?$", ip_str):
            return True
        messagebox.showerror("错误", "无效的IP地址格式！")
        return False
    
    def generate_targets(self, ip_str):
        """生成要扫描的目标列表"""
        if ip_str.count('.') == 3:  # 单个IP
            return [ip_str]
        # 添加网段格式验证
        if re.match(r"^\d{1,3}\.\d{1,3}\.\d{1,3}$", ip_str):
            return generate_ip_range(ip_str)
        messagebox.showerror("错误", "无效的网段格式！")
        return []
    
    def start_scan(self):
        """启动扫描线程"""
        target = self.ip_entry.get().strip()
        if not self.validate_input(target):
            return
        
        self.scan_button.config(state=tk.DISABLED)
        self.progress.pack(fill=tk.X, padx=10)
        self.progress.start()
        self.result_tree.delete(*self.result_tree.get_children())
        
        # 创建扫描线程
        scan_thread = threading.Thread(
            target=self.run_scan,
            args=(target,),
            daemon=True
        )
        scan_thread.start()
    
    def run_scan(self, target):
        """执行扫描任务"""
        try:
            targets = self.generate_targets(target)
            with concurrent.futures.ThreadPoolExecutor() as executor:
                futures = {executor.submit(scan_single_ip, ip): ip for ip in targets}
                for future in concurrent.futures.as_completed(futures):
                    result = future.result()
                    if result:
                        # 修正参数传递方式
                        self.master.after(0, lambda r=result: self.result_tree.insert(
                            '', 
                            tk.END, 
                            values=(r['ip'], r['mac'], r['platform'])
                        ))
        finally:
            self.master.after(0, self.scan_complete)

    
    def update_results(self, live_ips):
        """更新扫描结果到表格"""
        for info in live_ips:
            self.result_tree.insert('', tk.END, 
                values=(info['ip'], info['mac'], info['platform']))
    
    def scan_complete(self):
        """扫描完成处理"""
        self.progress.stop()
        self.progress.pack_forget()
        self.scan_button.config(state=tk.NORMAL)
        messagebox.showinfo("完成", "扫描任务已完成！")

# 检查IP是否存活
def ping_ip(ip):
    param = ["-n", "1", "-w", "1000"] if platform.system().lower() == "windows" else ["-c", "1", "-W", "1"]
    command = ["ping"] + param + [ip]
    try:
        response = subprocess.run(
            command,
            stdout=subprocess.DEVNULL,
            stderr=subprocess.DEVNULL,
            timeout=1,
            creationflags=subprocess.CREATE_NO_WINDOW if platform.system() == "Windows" else 0
        )
        return response.returncode == 0
    except subprocess.TimeoutExpired:
        return False


# 获取MAC地址
def get_mac(ip):
    command = ["arp", "-a", ip]
    result = subprocess.run(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)
    match = re.search(r"([a-fA-F0-9]{2}[:-]){5}[a-fA-F0-9]{2}", result.stdout)
    return match.group(0) if match else None

# 获取设备平台
def get_platform(ip):
    try:
        result = subprocess.run(["nmap", "-O", ip], stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)
        if "OS details" in result.stdout:
            os_match = re.search(r"OS details: (.*)", result.stdout)
            if os_match:
                return os_match.group(1)
    except Exception:
        return "Unknown"
    return platform.system()

# 扫描单个IP的功能
def scan_single_ip(ip):
    if ping_ip(ip):
        mac = get_mac(ip)
        platform_info = get_platform(ip)
        return {"ip": ip, "mac": mac, "platform": platform_info}
    return None

# 扫描IP段
def scan_network(ip_range):
    live_ips = []
    with concurrent.futures.ThreadPoolExecutor(max_workers=10) as executor:
        # 改为submit提交任务获取future对象
        futures = {executor.submit(scan_single_ip, ip): ip for ip in ip_range}
        # 添加实时结果处理
        for future in concurrent.futures.as_completed(futures):
            result = future.result()
            if result:
                live_ips.append(result)
    return live_ips

# 示例IP段生成器
def generate_ip_range(network):
    base_ip = network.split(".")
    ip_range = []
    for i in range(1, 255):
        ip_range.append(".".join(base_ip[:3] + [str(i)]))
    return ip_range


if __name__ == "__main__":
    root = tk.Tk()
    app = NetworkScannerGUI(root)
    root.geometry("800x600")
    root.mainloop()
